package tech.nuoson.wisdom.infra.ydaospeechtrans;

import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;

import org.springframework.lang.NonNull;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.client.WebSocketConnectionManager;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import lombok.extern.slf4j.Slf4j;
import tech.nuoson.wisdom.domain.foundationmodel.ApiErrorCode;
import tech.nuoson.wisdom.domain.foundationmodel.BusinessException;
import tech.nuoson.wisdom.domain.speechtrans.AbstractSpeechTransMessage;
import tech.nuoson.wisdom.domain.speechtrans.SpeechTransGateway;
import tech.nuoson.wisdom.domain.speechtrans.SpeechTransStateMessage;
import tech.nuoson.wisdom.infra.internal.ydaospeechtrans.YdaoAuthUtils;
import tech.nuoson.wisdom.infra.internal.ydaospeechtrans.YdaoSpeechTransWebSocketHandler;
import tech.nuoson.wisdom.infra.internal.ydaospeechtrans.YdaoV4SignResult;

/**
 * 有道语音翻译 api 连接器，用于有道 api 建立 websocket 连接并输出翻译结果
 */
@Slf4j
public class YdaoSpeechTransConnector implements SpeechTransGateway {
    // #region -- ** 私有变量 **

    /**
     * 翻译结果或翻译连接状态消息的回调
     */
    private Consumer<AbstractSpeechTransMessage<?>> onMessageCallback;

    /**
     * websocket 客户端连接
     */
    private final StandardWebSocketClient wsClient;
    /**
     * websocket 连接管理器
     */
    private final WebSocketConnectionManager wsClientManager;

    /**
     * websocket 连接消息/事件处理器
     */
    private final YdaoSpeechTransWebSocketHandler wsHandler;

    /**
     * websocket 异步连接状态指示器，用于等待连接完成或异常
     */
    private CompletableFuture<Void> connectFuture;
    // #endregion

    // #region [** Input Type ** ] -- ** 构造方法 **
    /**
     * 构造方法 <br />
     * 注：@NonNull 注解不会进行强制检测，更多的是作为一种文档和编译时检查的工具，<br />
     * 非空检查需要依赖于运行时的验证机制或者使用Bean Validation API
     * 
     * @param builder
     */
    private YdaoSpeechTransConnector(@NonNull Builder builder) {
        this.onMessageCallback = builder.onMessageCallback;
        this.wsClient = new StandardWebSocketClient();
        this.wsHandler = new YdaoSpeechTransWebSocketHandler((message) -> this.onWebsocketMessage(message));
        this.wsClientManager = new WebSocketConnectionManager(
                wsClient, wsHandler, this.buildConnectionUrl(builder));
    }
    // #endregion

    // #region -- ** 公共方法 **
    /**
     * 启动连接
     * 
     */
    @Override
    public CompletableFuture<Void> start() {
        log.debug("hashCode: {} start", this.hashCode());
        this.wsClientManager.start();
        // 异步指示器，用于调用等待连接结果
        this.connectFuture = new CompletableFuture<Void>();
        return this.connectFuture;
    }

    /**
     * 停止连接
     */
    @Override
    public void stop() {
        this.wsClientManager.stop();
    }

    /**
     * 发送一条语音消息
     * 
     * @param message
     */
    @Override
    public void sendSpeechMessage(@NonNull BinaryMessage message) {
        this.wsHandler.sendSpeechMessage(message);
    }
    // #endregion

    // #region -- ** 私有方法 **
    /**
     * 构建 websocket 连接 URL
     * 
     * @param builder
     * @return
     */
    private String buildConnectionUrl(Builder builder) {
        YdaoV4SignResult signResult = YdaoAuthUtils.getV4Sign(builder.appKey, builder.appSecret);
        return String.format(
                "wss://%s/stream_speech_trans?channel=1&version=v1&appKey=%s&salt=%s&curtime=%s&signType=%s&sign=%s&format=%s&rate=%s&from=%s&to=%s",
                builder.host,
                builder.appKey,
                signResult.getSalt(),
                signResult.getTimestamp(),
                signResult.getSignType(),
                signResult.getSign(),
                builder.format,
                builder.rate,
                builder.from,
                builder.to);

    }

    /**
     * websocket handler 事件监听，用于处理 handler 返回的 SpeechTransMessage
     * 
     * @param message
     */
    private void onWebsocketMessage(AbstractSpeechTransMessage<?> message) {
        switch (message.getType()) {
            case STATE:
                this.handleStateMessage((SpeechTransStateMessage) message);
                break;
            case TRANS_RESULT:
                // 转发翻译结果消息
                this.onMessageCallback.accept(message);
            default:
                break;
        }

    }

    /**
     * 处理状态消息
     * 
     * @param stateMessage
     */
    private void handleStateMessage(SpeechTransStateMessage stateMessage) {
        switch (stateMessage.getData().getCode()) {
            case SESSION_CONNECTED:
                // 通知连接成功
                this.connectFuture.complete(null);
                break;
            case SESSION_ERROR:
                // 通知连接失败，抛出异常
                this.connectFuture.completeExceptionally(new BusinessException(
                        ApiErrorCode.SpeechTransCodeEnum.WS_CONNECT_ERROR, stateMessage.getData().getMessage()));
                break;
            default:
                break;
        }
        // 转发消息
        this.onMessageCallback.accept(stateMessage);
    }
    // #endregion

    // #region -- Builder --

    /**
     * 构建器，用于构造 YdaoSpeechTransConnector 实例
     */
    public static class Builder {

        @NotBlank(message = "appKey不能为空")
        private String appKey;
        @NotBlank(message = "appSecret不能为空")
        private String appSecret;
        @NotBlank(message = "host不能为空")
        private String host;
        @NotBlank(message = "port不能为空")
        @Pattern(regexp = "^(zh-CHS|en|enzh)$")
        private String from;
        @NotBlank(message = "to不能为空")
        @Pattern(regexp = "^(zh-CHS|en|enzh)$")
        private String to;
        @NotBlank(message = "rate不能为空")
        private String rate = "16000";
        @NotBlank(message = "format不能为空")
        private String format = "wav";
        @NotBlank(message = "channel不能为空")
        private String channel = "1";
        @NotBlank(message = "version不能为空")
        private String version = "v1";
        @NotNull(message = "onMessageCallback不能为空")
        private Consumer<AbstractSpeechTransMessage<?>> onMessageCallback;

        public Builder appKey(String value) {
            appKey = value;
            return this;
        }

        public Builder appSecret(String value) {
            appSecret = value;
            return this;
        }

        public Builder host(String value) {
            host = value;
            return this;
        }

        public Builder from(String value) {
            from = value;
            return this;
        }

        public Builder to(String value) {
            to = value;
            return this;
        }

        public Builder rate(String value) {
            rate = value;
            return this;
        }

        public Builder format(String value) {
            format = value;
            return this;
        }

        public Builder channel(String value) {
            channel = value;
            return this;
        }

        public Builder version(String value) {
            version = value;
            return this;
        }

        public Builder onMessageCallback(Consumer<AbstractSpeechTransMessage<?>> value) {
            onMessageCallback = value;
            return this;
        }

        /**
         * 注：使用 jakarta.validation.Validator 对入参进行验证
         * 
         * @return
         */
        public YdaoSpeechTransConnector build() {
            // 验证入参
            try (ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory()) {
                Validator validator = validatorFactory.getValidator();
                Set<ConstraintViolation<Builder>> violations = validator.validate(this);
                if (violations != null && !violations.isEmpty()) {
                    StringBuilder errorMsgs = new StringBuilder();
                    violations.forEach(item -> {
                        errorMsgs.append(item.getMessage()).append(";");
                    });
                    throw new BusinessException(
                            ApiErrorCode.SpeechTransCodeEnum.WS_BUILDER_ARGS_ERROR,
                            errorMsgs.toString());
                }
                // 构建 YdaoSpeechTransConnector 实例
                return new YdaoSpeechTransConnector(this);
            }
        }
    }
    // #endregion
}
